﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ComponentModel;

namespace PDX
{
    /// <summary>
    /// A default <see cref="IPropertyModelFactory"/>.
    /// Creates <see cref="DefaultPropertyModel{T}"/>s.
    /// </summary>
    public class DefaultPropertyModelFactory : IPropertyModelFactory
    {
        /// <summary>
        /// Gets or sets the default instance.
        /// </summary>
        /// <value>
        /// The default instance.
        /// </value>
        public static IPropertyModelFactory DefaultInstance { get; set; }

        static DefaultPropertyModelFactory()
        {
            DefaultInstance = new DefaultPropertyModelFactory();
        }

        /// <summary>
        /// A convenient way (from XAML) to make this the <see cref="DefaultInstance"/>.
        /// WARNING! Getter returns false! Setting to false does nothing!
        /// </summary>
        /// <value>
        ///   <c>true</c> to make this the <see cref="DefaultInstance"/>.
        /// </value>
        public bool MakeDefault
        {
            get { return false; }
            set
            {
                if (value)
                {
                    DefaultInstance = this;
                }
            }
        }

        /// <summary>
        /// Creates the property model.
        /// </summary>
        /// <param name="resolution">The resolution.</param>
        /// <returns></returns>
        public virtual object CreatePropertyModel(IPropertyResolution resolution)
        {
            var type = typeof(DefaultPropertyModel<>).MakeGenericType(resolution.Property.PropertyType);
            var pm = Activator.CreateInstance(type, new object[] { resolution }) as IPropertyModel;

            HandleValidation(pm);

            return pm;
        }

        #region "Validation"

        /// <summary>
        /// Handles the creation and wire-up of the
        /// <see cref="IValidationAdapter"/> and <see cref="ValidationModel"/>.
        /// </summary>
        /// <param name="propModel">The prop model.</param>
        protected virtual void HandleValidation(IPropertyModel propModel)
        {
            // get or create a ValidationAdapter
            var adapter = GetExistingValidationAdapter(propModel);
            if (adapter == null)
            {
                adapter = CreateValidationAdapter(propModel);

                if (adapter != null)
                {
                    AttachValidationAdapterToViewmodel(propModel, adapter);
                }
            }

            if (adapter == null)
            {
                return;
            }

            // create the ValidationModel
            var model = CreateValidationModel(propModel, adapter);

            // attach the adapter to the PropertyModel
            if (model != null)
            {
                AttachValidationModelToPropertyModel(propModel, model);
            }
        }

        /// <summary>
        /// Gets an existing ValidationAdapter, if any.
        /// </summary>
        /// <param name="propModel">The property model.</param>
        /// <returns></returns>
        protected virtual IValidationAdapter GetExistingValidationAdapter(IPropertyModel propModel)
        {
            var metadata = ViewmodelMetadata.Get(propModel);
            return metadata.ValidationAdapter;
        }

        /// <summary>
        /// Creates the ValidationAdapter.
        /// </summary>
        /// <param name="propModel">The property model.</param>
        /// <returns></returns>
        protected virtual IValidationAdapter CreateValidationAdapter(IPropertyModel propModel)
        {
            var metadata = ViewmodelMetadata.Get(propModel);
            var adapter = new DefaultValidationAdapter(metadata);
            return adapter;
        }

        /// <summary>
        /// Attaches the validation adapter to viewmodel.
        /// </summary>
        /// <param name="propModel">The property model.</param>
        /// <param name="adapter">The adapter.</param>
        protected virtual void AttachValidationAdapterToViewmodel(IPropertyModel propModel, IValidationAdapter adapter)
        {
            var metadata = ViewmodelMetadata.Get(propModel);
            metadata.ValidationAdapter = adapter;
        }

        /// <summary>
        /// Creates the validation model.
        /// </summary>
        /// <param name="propModel">The property model.</param>
        /// <param name="adapter">The adapter.</param>
        /// <returns></returns>
        protected virtual object CreateValidationModel(IPropertyModel propModel, IValidationAdapter adapter)
        {
            var model = new ValidationModel(adapter, propModel);
            return model;
        }

        /// <summary>
        /// Attaches the validation model to property model.
        /// </summary>
        /// <param name="propModel">The property model.</param>
        /// <param name="validationModel">The validation model.</param>
        protected virtual void AttachValidationModelToPropertyModel(IPropertyModel propModel, object validationModel)
        {
            propModel.Ext["Validation"] = validationModel;
        }

        #endregion
    }
}
